#include <at89x52.h>
#include "main.h"
#include "tm1668.h"

#define LED P1_7

#define SDA P3_2
#define SCL P3_3

#define FAN P1_3
#define RELAY0 P1_1
#define RELAY1 P1_0

__sfr __at (0x92) P1M0 ;

//sht10_xxx() from sht10.c
void sht10_init(void);
void sht10_update(void);

val_t cur_temp, cur_humi, goal_temp, goal_temp_tmp;
static int8_t half_read = 0;

void led_delay(void)
{
    uint32_t i;
    for(i=0;i<100000L;i++)
        ;
}

/*
 * 0: success, -1: timeout
 */
static int8_t waiting_pulse_sda(int8_t to_val, uint32_t timeout)
{
    uint32_t i;
    int virgin = 1;
    for (i = 0; i < timeout; ++i) {
        if ((SDA == to_val) && virgin) continue;
        virgin = 0;
        if (SDA != to_val) continue;
        return 0;
    }
    return -1;
}
static int8_t waiting_pulse_scl(int8_t to_val, uint32_t timeout)
{
    uint32_t i;
    int8_t virgin = 1;
    for (i = 0; i < timeout; ++i) {
        if ((SCL == to_val) && virgin) continue;
        virgin = 0;
        if (SCL != to_val) continue;
        return 0;
    }
    return -1;
}

static int8_t i2c_rx(uint8_t *val)
{
    int8_t i;
    for (i = 0; i < 8; ++i) {
        if (waiting_pulse_scl(0, 50000) == -1) return -1;
        *val = (*val << 1) + SDA;
    }
    if (waiting_pulse_scl(1, 50000) == -1) return -1;
    if (SDA == 1) SDA = 0; // send ack
    else return -1;
    if (waiting_pulse_scl(0, 50000) == -1) {SDA = 1; return -1;}
    SDA = 1; // ack done
    return 0;
}

static int8_t i2c_tx(uint8_t *val)
{
    int8_t i;
    for (i = 0; i < 8; ++i) {
        if (waiting_pulse_scl(1, 50000) == -1) goto error;
        SDA =  (*val >> (7 - i)) & 0x01;
    }
    if (waiting_pulse_scl(0, 50000) == -1) goto error;
    SDA = 1;

    if (waiting_pulse_scl(0, 50000) == -1) goto error;
    if (SDA == 1) goto error; // check ACK from server
    if (waiting_pulse_sda(1, 50000) == -1) goto error; // wait end of ACK
    return 0;
 error:
    SDA = 1;
    return -1;
}

/*
 * read is not reported, return -1
 * write return the reg addr , else return -1
 */
static int8_t i2c_listen(void)
{
    uint8_t addr; //    _
    int8_t is_read; //R/W : 0 = write
    uint8_t *p;

    if (waiting_pulse_sda(0, 50000) == -1) return -1; // wait start phase
    if (SCL != 1) return -1;
    if (waiting_pulse_scl(0, 50000) == -1) return -1; // start phase end

    if (i2c_rx(&addr) == -1) return -1;

    is_read = addr & 0x01;
    addr /= 2;

    if (is_read) {
        switch (addr) {
        case 0 : p = &goal_temp.uc[0]; break;
        case 1 : p = &goal_temp.uc[1]; break;

        case 2 : p = &cur_temp.uc[0]; half_read = 1; break;
        case 3 : p = &cur_temp.uc[1]; half_read = 0; break;
        case 4 : p = &cur_humi.uc[0]; half_read = 1; break;
        case 5 : p = &cur_humi.uc[1]; half_read = 0; break;
        default : return -1;
        }
        if (i2c_tx(p) == -1) return -1;
    } else {
        switch (addr) {
        case 0 : p = &goal_temp_tmp.uc[0]; break;
        case 1 : p = &goal_temp_tmp.uc[1]; break;
        default : return -1;
        }
        if (i2c_rx(p) == -1) return -1;
    }

    waiting_pulse_sda(1, 50000);
    if (!is_read) return addr;
    return 0;
}

static uint8_t fan_delay = 0;

static void set_power(int8_t level)
{
    switch (level) {
    case 1:
        RELAY1 = 0;
        FAN = RELAY0 = 1;
        tm1668_led &= ~TM_LED_M_U;
        tm1668_led |= TM_LED_M_D;
        break;
    case 2:
        FAN = RELAY0 = RELAY1 = 1;
        tm1668_led |= TM_LED_M_U | TM_LED_M_D;
        break;
    default:
        if (RELAY0 || RELAY1) {
            RELAY0 = RELAY1 = 0;
            fan_delay = 0xff; // FAN = 0 delayed
            tm1668_led &= ~(TM_LED_M_U | TM_LED_M_D);
        }
    }
    tm1668_update();
}

enum TempState {state_under = 0, state_middle, state_over};

void main(void)
{
    uint8_t i;
    int8_t display_current = 0; // toggle display for tm1668
    enum TempState temp_state = state_under;

    //SDA = SCL = 1;

    RELAY0 = RELAY1 = FAN = 0;
    P1M0 = 0x0b; // set push-pull

    for (i = 0; i < 5; ++i) {
        LED = 0; // on
        led_delay();
        LED = 1; // off
        led_delay();
    }

    tm1668_init();

    while (!(SDA && SCL)); // wait for host

    sht10_init();

    goal_temp.si = 1400; // set default

    i = 0;
    while (1) {
        if (!RELAY0) {
            if (fan_delay != 0)
                fan_delay--;
            else
                FAN = 0;
        }
        LED = 1;
        if (!half_read) {
            sht10_update();
            ++i;
            if (display_current) {
                //tm1668_num = (cur_temp.si + 0.5) / 100;
                tm1668_num = cur_temp.si * 0.01;
                tm1668_led |= TM_LED_L_G;
                tm1668_led &= ~TM_LED_R_B;
            } else {
                //tm1668_num = (goal_temp.si + 0.5) / 100;
                tm1668_num = goal_temp.si * 0.01;
                tm1668_led &= ~TM_LED_L_G;
                tm1668_led |= TM_LED_R_B;
            }
            if (i > 3) {
                i = 0;
                display_current = !display_current;
            }
            tm1668_update();
        }
        LED = 0;
        if (i2c_listen() == 1)
            goal_temp.si = goal_temp_tmp.si;
        if (cur_temp.si < goal_temp.si - 100) {
            temp_state = state_under;
            set_power(2); //
        } else if (cur_temp.si > goal_temp.si + 100) {
            temp_state = state_over;
            set_power(0); //
        } else {
            switch (temp_state) {
            case state_under:
                if (cur_temp.si > goal_temp.si) {
                    temp_state = state_middle;
                    set_power(1); //
                }
                break;
            case state_over:
                if (cur_temp.si < goal_temp.si) {
                    temp_state = state_middle;
                    set_power(1); //
                }
                break;
            default: // temp_middle
                break;
            }
        }
    }
}
